package RROcr

import (
	"cmp"
	"encoding/json"
	"github.com/google/go-querystring/query"
	"github.com/kaylee595/tracerr"
	"io"
	"net/http"
)

type Option func(*Client)

func WithShareCode(shareCode string) Option {
	return func(client *Client) {
		client.shareCode = shareCode
	}
}

type Client struct {
	httpClient *http.Client
	appKey     string
	shareCode  string
}

// NewClient appkey去他们官网搞: http://www.rrocr.com/
func NewClient(httpClient *http.Client, appKey string, options ...Option) *Client {
	c := &Client{
		httpClient: cmp.Or(httpClient, http.DefaultClient),
		appKey:     appKey,
	}
	for _, option := range options {
		option(c)
	}
	return c
}

type ReqRecognize struct {
	Referer    string `url:"referer"`     //	必填	验证码所在的页面URL
	Gt         string `url:"gt"`          //	必填	验证码所在网站极验的gt值，如果是四代的话gt参数为captcha_id值
	Challenge  string `url:"challenge"`   //	选填	通过请求目标站配置项获取，每次获取到的challenge仅能使用一次。如网站为极验感知无此参数可不填，如果是四代的话challenge无需传
	MmtKey     string `url:"mmt_key"`     //	选填	特定网站所需的参数，四代个别定制板如有此参数需要传此参数，如定制版包里有mmt_key需要传此参数
	RiskType   string `url:"risk_type"`   //	选填	特定网站所需的参数，四代个别定制板如有此参数需要传此参数，抓到的包中,load请求参数中有则需要填,无则不需要填
	ProxyIp    string `url:"proxy_ip"`    //	选填	注意此参数原ip字段废弃更名为proxy_ip，代理ip参数，如果有些网站认ip的话，可传此参数注意代理ip格式为：【ip:port】，如果代理ip是ip白名单的，需要把此ip【27.151.28.63】添加到白名单，如果未添加白名单或者账号密码有问题统一返回105
	IpAccount  string `url:"ip_account"`  //	选填	代理ip鉴权的账号，如果代理ip为白名单无需传此参数
	IpPassword string `url:"ip_password"` //	选填	代理ip鉴权的密码，如果代理ip为白名单无需传此参数
	Host       string `url:"host"`        //	选填	特殊网站极验抓包为三级域名的比如api-na.geetest.com这样的需要传对应的域名需要传此参数，注意不要带【http://】和【/】只需要域名即可
	UserAgent  string `url:"useragent"`   //	选填	自定义UA，适用于某些检验UA的网站，此参数需要URL编码
}

type ApiBase struct {
	Status int       `json:"status"`
	Msg    string    `json:"msg"`
	Time   int       `json:"time"`
	Code   ErrorCode `json:"code"`
}

func (a *ApiBase) IsError() bool {
	return a.Status != 0 || a.Code != 0
}

func (a *ApiBase) Error() string {
	return a.Msg
}

type RespRecognizeOfDataSecCode struct {
	CaptchaId     string `json:"captcha_id"`
	LotNumber     string `json:"lot_number"`
	PassToken     string `json:"pass_token"`
	GenTime       string `json:"gen_time"`
	CaptchaOutput string `json:"captcha_output"`
}

type RespRecognizeOfData struct {
	Challenge string                     `json:"challenge"` // 三代和四代
	Validate  string                     `json:"validate"`  // 三代
	SecCode   RespRecognizeOfDataSecCode `json:"seccode"`   // 四代
}

type RespRecognize struct {
	ApiBase
	Data RespRecognizeOfData `json:"data"`
}

// Recognize 识别接口
func (c *Client) Recognize(req *ReqRecognize) (*RespRecognize, error) {
	const addr = "http://api.rrocr.com/api/recognize.html"
	// 将参数从结构体转为values
	if req == nil {
		return nil, tracerr.New("empty req")
	}
	reqPayload, err := query.Values(req)
	if err != nil {
		return nil, tracerr.Wrap(err)
	}
	// 往请求体添加一些必须值
	reqPayload.Set("appkey", c.appKey)
	reqPayload.Set("sharecode", c.shareCode)
	// 发送请求
	resp, err := c.httpClient.PostForm(addr, reqPayload)
	if err != nil {
		return nil, tracerr.Wrap(err)
	}
	defer resp.Body.Close()
	respPayloadBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, tracerr.Wrap(err)
	}
	respPayload := new(RespRecognize)
	err = json.Unmarshal(respPayloadBytes, respPayload)
	if err != nil {
		return nil, tracerr.Errorf("请求体解析出错, %w, 请求体: %s", err, respPayloadBytes)
	}
	if respPayload.IsError() {
		return nil, respPayload
	}
	return respPayload, nil
}
